import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { parseHeaders } from '../helpers/headers'
import { createError } from '../helpers/error'

export function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => { // 返回Promise<AxiosResponse>
    const { url, method = 'get', data = null, headers, responseType, timeout } = config

    const request = new XMLHttpRequest() // 获取xhr对象

    if (responseType) { // 设置响应类型，会决定之后返回的data的类型
      request.responseType = responseType
    }

    if (timeout) { // 设置超时时间
      request.timeout = timeout
    }


    request.open(method.toLocaleUpperCase(), url!, true) // 设置请求动词，方法

    Object.keys(headers).forEach((name) => { // 设置resquest的headers
      if (data === null && name.toLowerCase() === 'content-type') {
        // 若是data为空则content-type是无意义的
        delete headers[name]
      } else {
        request.setRequestHeader(name, headers[name])
      }
    })

    request.send(data) // 发送请求

    request.onreadystatechange = function handleLoad() { // 监听发送状态
      if (request.readyState !== 4) return
      if (request.status === 0) return

      const responseHeaders = parseHeaders(request.getAllResponseHeaders()) // 设置转化好的responseheaders
      const responseData = responseType === 'text' ? request.responseText : request.response // 根据响应类型决定返回的data为text还是对象
      const response: AxiosResponse = { // 返回封装好的response
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config,
        request,
      }

      handleResponse(response)
    }

    request.onerror = function handleError() { // 监听网络异常
      reject(createError('Network Error', config, null, request))
    }

    request.ontimeout = function handleTimeout() { // 监听超时
      reject(createError(`Timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED', request))
    }

    function handleResponse(response: AxiosResponse): void { // 处理返回结果
      if (response.status >= 200 && response.status < 300) { // 判断status异常
        resolve(response)
      } else {
        reject(createError(`Request failed with status code ${response.status}`, config, null, request, response))
      }
    }
  })
}
